package magnet.processor.registry.instances;

import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.CodeBlock;

import java.util.HashMap;
import java.util.Map;

public class IndexGeneratorVisitor implements Model.IndexVisitor {

    private CodeBlock.Builder indexBuilder = CodeBlock.builder();
    private CodeBlock.Builder targetsBuilder = CodeBlock.builder();

    private ClassName rangeClassName = ClassName.bestGuess("magnet.internal.Range");
    private boolean generateSingleRange = false;
    private Model.Section currentSection = null;
    private int sectionIndex = 0;

    @Override
    public void visit(Model.Inst inst) {
        // nop
    }

    @Override
    public void visit(Model.Index index) {
        // nop
    }

    @Override
    public void visit(Model.Section section) {
        generateSingleRange = section.ranges.size() == 1;
        currentSection = section;

        if (generateSingleRange) {
            return;
        }

        String targetsName = "ranges" + ++sectionIndex;

        indexBuilder.addStatement(
                "index.put($T.getType(), $L)",
                section.getFirstFactory(),
                targetsName
        );

        int mapSize = Math.max(Math.round(section.ranges.size() / .75f), 8);
        targetsBuilder.addStatement(
                "$T<$T, $T> $L = new $T<>(" + mapSize + ")",
                Map.class,
                String.class,
                rangeClassName,
                targetsName,
                HashMap.class
        );
    }

    @Override
    public void visit(Model.Range range) {
        if (generateSingleRange) {
            if (currentSection != null) {
                indexBuilder.addStatement(
                        "index.put($T.getType(), new $T($L, $L, $S))",
                        range.getFirstFactory(),
                        rangeClassName,
                        range.getFrom(),
                        range.getImpls().size(),
                        range.getClassifier()
                );
            }
            return;
        }

        String targetsName = "ranges" + sectionIndex;

        targetsBuilder.addStatement(
                "$L.put($S, new $T($L, $L, $S))",
                targetsName,
                range.getClassifier(),
                rangeClassName,
                range.getFrom(),
                range.getImpls().size(),
                range.getClassifier()
        );
    }

    public CodeBlock.Builder getIndexBuilder() {
        return indexBuilder;
    }

    public CodeBlock.Builder getTargetsBuilder() {
        return targetsBuilder;
    }
}
